#pragma once
#include <global_definitions.hpp>
#include <string>
#include <unordered_map>
#include <optional>
#include <type_system/type_system_classes.hpp>

namespace blitz_query_cpp
{
    class schema_t
    {
    protected:
        const object_type *query_type = nullptr;
        const object_type *mutation_type = nullptr;
        const object_type *subscription_type = nullptr;
        // introspection support
        const object_type *type_type = nullptr;
        const object_type *type_kind_type = nullptr;
        const object_type *field_type = nullptr;
        const object_type *input_value_type = nullptr;
        const object_type *enum_type = nullptr;
        const object_type *directive_type_type = nullptr;
        const object_type *directive_location_type = nullptr;

    public:
        named_collection<object_type> types;
        named_collection<directive_type> directive_types;
        std::vector<directive> directives;

        std::string query_type_name;
        std::string mutation_type_name;
        std::string subscription_type_name;

        std::optional<std::string> description;

    public:
        const directive_type *find_directive_type(std::string_view name) const
        {
            auto dir = std::find_if(directive_types.begin(), directive_types.end(),
                                    [name](auto &dir)
                                    {
                                        return dir.name == name;
                                    });
            if (dir == directive_types.end())
                return nullptr;
            return &*dir;
        }

        const object_type *find_type(std::string_view name) const
        {
            auto type = types.find(name);
            if (type == types.end())
                return nullptr;
            return &*type;
        }

        const object_type *get_query_type() const { return query_type; }
        const object_type *get_mutation_type() const { return mutation_type; }
        const object_type *get_subscription_type() const { return subscription_type; }

        const object_type *get_type_type() const { return type_type; }
        const object_type *get_type_kind_type() const { return type_kind_type; }
        const object_type *get_field_type() const { return field_type; }
        const object_type *get_input_value_type() const { return input_value_type; }
        const object_type *get_enum_type() const { return enum_type; }
        const object_type *get_directive_type_type() const { return directive_type_type; }
        const object_type *get_directive_location_type() const { return directive_location_type; }
        std::string_view get_description() const { return description.has_value() ? std::string_view{description.value()} : std::string_view{}; }

        void update_types()
        {
            query_type = find_type(query_type_name);
            mutation_type = find_type(mutation_type_name);
            subscription_type = find_type(subscription_type_name);
            
            type_type = find_type("__Type");
            type_kind_type = find_type("__TypeKind");
            field_type = find_type("__Field");
            input_value_type = find_type("__InputValue");
            enum_type = find_type("__EnumValue");
            directive_type_type = find_type("__Directive");
            directive_location_type = find_type("__DirectiveLocation");
        }
    };
}